package com.peak.distribution.config;

import java.util.Properties;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.peak.spring.boot.core.repository.BaseRepositoryImpl;
import com.peak.spring.boot.tools.Converts;

/**
 * JPA配置类 value是设置扫描哪个包下的类，感觉必须定义，否则找不到repostoiry repositoryBaseClass的目的是可以自定义方法的实现，不需要必须按着已给的方法来，如果没有自定义方法，可以不写这个
 * 
 * @author Haley Gao
 * 
 */
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(value = "${entitymanager.repository.packagesToScan}", repositoryBaseClass = BaseRepositoryImpl.class)
public class SpringDataJpaConfig {

	@Autowired
	private Environment env;

	@Autowired
	private DataSource dataSource;

	@Autowired
	private LocalContainerEntityManagerFactoryBean entityManagerFactory;

	/**
	 * DataSource definition for database connection. Settings are read from the application.properties file (using the
	 * env object).
	 */
	@Bean
	public DataSource dataSource() {
		PoolProperties poolConfig = new PoolProperties();
		DataSource dataSource = new DataSource(poolConfig);
		poolConfig.setUrl(env.getProperty("db.url"));
		poolConfig.setUsername(env.getProperty("db.username"));
		poolConfig.setPassword(env.getProperty("db.password"));
		poolConfig.setDriverClassName(env.getProperty("db.driver"));
		poolConfig.setInitialSize(Converts.toInt(env.getProperty("jdbc.pool.initialSize", "1")));
		poolConfig.setMinIdle(Converts.toInt(env.getProperty("jdbc.pool.minIdle", "0")));
		poolConfig.setMaxIdle(Converts.toInt(env.getProperty("jdbc.pool.maxIdle", "10")));
		poolConfig.setMaxActive(Converts.toInt(env.getProperty("jdbc.pool.maxActive", "50")));
		poolConfig.setTimeBetweenEvictionRunsMillis(Converts.toInt(env.getProperty("jdbc.pool.timeBetweenEvictionRunsMillis", "60000")));
		poolConfig.setMinEvictableIdleTimeMillis(Converts.toInt(env.getProperty("jdbc.pool.minEvictableIdleTimeMillis", "600000")));
		return dataSource;
	}

	/**
	 * Declare the JPA entity manager factory.
	 */
	@Bean
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
		// dataSource, Tomcat Data Pool
		entityManagerFactory.setDataSource(dataSource);

		// Classpath scanning of @Component, @Service, etc annotated class
		String[] pagckages = env.getProperty("entitymanager.entity.packagesToScan").split(",");
		entityManagerFactory.setPackagesToScan(pagckages);

		// Vendor adapter
		HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		entityManagerFactory.setJpaVendorAdapter(vendorAdapter);

		// Hibernate properties
		Properties additionalProperties = new Properties();
		additionalProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
		additionalProperties.put("hibernate.implicit_naming_strategy", env.getProperty("hibernate.implicit_naming_strategy"));
		additionalProperties.put("hibernate.physical_naming_strategy", env.getProperty("hibernate.physical_naming_strategy"));
		additionalProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
		additionalProperties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
		// additionalProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
		additionalProperties.put("hibernate.max_fetch_depth", env.getProperty("hibernate.max_fetch_depth"));
		additionalProperties.put("hibernate.jdbc.fetch_size", env.getProperty("hibernate.jdbc.fetch_size"));
		additionalProperties.put("hibernate.jdbc.batch_size", env.getProperty("hibernate.jdbc.batch_size"));
		additionalProperties.put("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
		additionalProperties.put("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));
		additionalProperties.put("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
		entityManagerFactory.setJpaProperties(additionalProperties);

		return entityManagerFactory;
	}

	/**
	 * Declare the transaction manager.
	 */
	@Bean
	public JpaTransactionManager transactionManager() {
		JpaTransactionManager transactionManager = new JpaTransactionManager();
		transactionManager.setEntityManagerFactory(entityManagerFactory.getObject());
		return transactionManager;
	}

	/**
	 * PersistenceExceptionTranslationPostProcessor is a bean post processor which adds an advisor to any bean annotated
	 * with Repository so that any platform-specific exceptions are caught and then rethrown as one Spring's unchecked
	 * data access exceptions (i.e. a subclass of DataAccessException).
	 */
	@Bean
	public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
		return new PersistenceExceptionTranslationPostProcessor();
	}
	
}
